package main

import (
	"gogl/pixel"
	"github.com/go-gl/gl/v2.1/gl"
	mgl "github.com/go-gl/mathgl/mgl32"
	"gogl/array"
	"gogl/shader"
)

var cube *Cube
var cubePositions  = []mgl.Vec3{
	{ 0.0,  0.0,  0.0},
	{ 2.0,  5.0, -15.0},
	{-1.5, -2.2, -2.5},
	{-3.8, -2.0, -12.3},
	{ 2.4, -0.4, -3.5},
	{-1.7,  3.0, -7.5},
	{ 1.3, -2.0, -2.5},
	{ 1.5,  2.0, -2.5},
	{ 1.5,  0.2, -1.5},
	{-1.3,  1.0, -1.5},
}
type Cube struct{
	prog *shader.Program
	vao,vbo uint32
	points []float32
	indices []uint32
}
func (c *Cube) construct(){
	c.setShaderSource()
	c.makeTexture()
	c.makeVao()
}

func (c *Cube) draw(){
	prog := c.prog.Use()
	prog.Bind1f("opcity",opcity)
	prog.Bind3f("light_colour",1,1,1)
	prog.Bind3f("material.ambient",1,0.5,0.31)
	prog.Bind1i("material.diffuse",0)
	prog.Bind1i("material.specular",1)
	prog.Bind1f("material.shininess",32)
	prog.Bind3fv("light.position",1,&lightPos[0])
	lightColor := mgl.Vec3{1,1,1}
	diffuseColor := lightColor.Mul(0.5)
	ambientColor := diffuseColor.Mul(0.2)
	prog.Bind3fv("light.ambient",1,&ambientColor[0])
	prog.Bind3fv("light.diffuse",1,&diffuseColor[0])
	prog.Bind3f("light.specular",1,1,1)
	prog.Bind3fv("viewPos",1,&ca.Position[0])

	projection := ca.GetProjection()

	view := ca.GetViewMatrix()

	prog.Bind4fv("view",1,false,&view[0])
	prog.Bind4fv("projection",1,false,&projection[0])

	gl.BindVertexArray(c.vao)
	for pk,pv := range cubePositions{
		angle := 20 * float32(pk)
		model := mgl.Translate3D(pv.X(),pv.Y(),pv.Z())
		model = model.Mul4(mgl.HomogRotate3D(mgl.DegToRad(angle),mgl.Vec3{1.0,0,0}))
		prog.Bind4fv("model",1,false,&model[0])
		gl.DrawArrays(gl.TRIANGLES, 0, int32(len(c.points)))
	}


}
func (c *Cube) setShaderSource(){
	const (
		vertexShaderSource = `
        #version 120
		 attribute vec3 vp;
        attribute vec3 aColor;
		 attribute vec2 aTexCoords;
		 attribute vec3 aNormal;
		 varying vec3 Normal;
		 varying vec3 FragPos;
        varying vec2 TexCoords;
		 uniform mat4 model;
		 uniform mat4 view;
		 uniform mat4 projection;
        void main() {
			gl_Position = projection*view*model*vec4(vp, 1.0);
			TexCoords = aTexCoords;
			FragPos = vec3(model *vec4(vp,1.0));
			Normal = gl_NormalMatrix *vec3(model *vec4(aNormal,1.0));
        }
    ` + "\x00"
	 fragmentShaderSource = `
        #version 120
        uniform vec3 light_colour;
		 varying vec3 Normal;
		 uniform vec3 viewPos;
        uniform float opcity;
		 varying vec3 FragPos;
		 varying vec2 TexCoords;
		 struct Material {
    		vec3 ambient;
    		sampler2D diffuse;
    		sampler2D specular;
    		float shininess;
		 };
		 struct Light {
    		vec3 position;
    		vec3 ambient;
    		vec3 diffuse;
    		vec3 specular;
		 };

		 uniform Material material;
		 uniform Light light;
        void main() {
			vec3 ambient = light.ambient * texture2D(material.diffuse, TexCoords).rgb;

		   vec3 norm = normalize(Normal);
          vec3 lightDir = normalize(light.position - FragPos);
			float diff = max(dot(norm, lightDir), 0.0);
			vec3 diffuse =  light.diffuse*diff*texture2D(material.diffuse, TexCoords).rgb;

			vec3 viewDir = normalize(viewPos - FragPos);
			vec3 reflectDir = -lightDir + 2*dot(lightDir,norm)*norm;
			float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
			vec3 specular = light.specular * spec * texture2D(material.specular, TexCoords).rgb;

			vec3 result = ambient+diffuse+specular;
			gl_FragColor = vec4(result,1.0);
        }
    ` + "\x00"
	)
	//顶点着色器
	vertexShader, err := shader.CompileShader(vertexShaderSource, gl.VERTEX_SHADER)
	if err != nil {
		panic(err)
	}
	fragmentShader, err := shader.CompileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
	if err != nil {
		panic(err)
	}

	c.prog = shader.NewProgram(vertexShader,fragmentShader)
	//编译完成后需要删除着色器
	gl.DeleteShader(vertexShader)
	gl.DeleteShader(fragmentShader)
}
func (c *Cube) makeTexture(){
	texture1 := pixel.MakeTexture("images/container2.png")
	texture2 := pixel.MakeTexture("images/container2_specular.png")
	texture3 := pixel.MakeTexture("images/matrix.jpg")
	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, texture1)
	gl.ActiveTexture(gl.TEXTURE1)
	gl.BindTexture(gl.TEXTURE_2D, texture2)
	gl.ActiveTexture(gl.TEXTURE2)
	gl.BindTexture(gl.TEXTURE_2D,texture3)

}
//索引缓冲
func (c *Cube) makeVao(){

	var (
		points = []float32{
			0.5, 0.5, 0.5, 	1.0, 0.0, 0.0,    0,0,	0,0,0,    // 右上角
			0.5, -0.5, 0.5,  0.0, 1.0, 0.0,   0,0, 0,0,0,// 右下角
			-0.5, -0.5, 0.5,  0.0, 0.0, 1.0,  0,0, 	0,0,0,// 左下角
			-0.5, 0.5,0.5,   0.0, 0.0, 1.0,   0,0,  0,0,0,// 左上角
			0.5, 0.5, -0.5,  1.0, 0.0, 0.0,   0,0,  0,0,0,    // 右上角
			0.5, -0.5, -0.5,  0.0, 1.0, 0.0,  0,0,  0,0,0,// 右下角
			-0.5, -0.5, -0.5,  0.0, 0.0, 1.0, 0,0, 	0,0,0,// 左下角
			-0.5, 0.5,-0.5,   0.0, 0.0, 1.0, 0,0,  0,0,0,// 左上角
		};
		indices = []uint32{
			0, 2, 1,
			2, 3, 0,
			1, 2, 5,
			2, 5, 6,
			2, 6, 3,
			3, 6, 7,
			3, 4, 7,
			0, 3, 4,
			0, 4, 5,
			0, 1, 5,
			4, 6, 7,
			4, 5, 6,
		};
	)
	var slice_len int32 = 11
	//绑定法向量
	array.GetIndicesNormal(&points,indices,slice_len,8,9,10)
	newpoints := array.IndicesToPoints(points,indices,slice_len)
	array.SetTextureCo(&newpoints,points,indices,slice_len,6,7)

	var vbo uint32

	gl.GenBuffers(1,&vbo)
	gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
	gl.BufferData(gl.ARRAY_BUFFER,4*len(newpoints), gl.Ptr(newpoints), gl.STATIC_DRAW)
	var vao uint32
	gl.GenVertexArrays(1,&vao)
	gl.BindVertexArray(vao)
	//gl.PolygonMode(gl.FRONT_AND_BACK, gl.LINE)
	gl.BindBuffer(gl.ARRAY_BUFFER,vbo)

	prog := c.prog
	prog.SetTotalAttributeNum(slice_len)

	prog.BindAttribute("vp",3)
	prog.BindAttribute("aColor",3)
	prog.BindAttribute("aTexCoords",2)
	prog.BindAttribute("aNormal",3)

	gl.Enable(gl.DEPTH_TEST)
	gl.DepthFunc(gl.LESS)

	c.vao = vao
	c.vbo = vbo
	c.points = newpoints
	c.indices = indices
}

func DrawCube(){
	if cube == nil{
		cube = &Cube{}
		cube.construct()
	}
	cube.draw()
}

func DestroyCube() {
	gl.DeleteVertexArrays(1,&cube.vao)
	gl.DeleteBuffers(1,&cube.vbo)
}

